package com.seari.ats.handler;

import com.seari.bean.ats.*;
import com.seari.utils.DataUtil;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;

import com.seari.ats.AtsClient;
import com.seari.ats.Reconnection;
import com.seari.utils.JedisPoolUtils;

import java.util.*;

/**
 * @author TOUC 处理器
 *
 */
public class AtsHandler extends ChannelInboundHandlerAdapter {

	protected static Logger logger = LoggerFactory.getLogger(AtsHandler.class);

	AtsClient client;

	//获取jedis连接
	Jedis jedis = JedisPoolUtils.getJedis();


	public AtsHandler(AtsClient client) {
		this.client = client;
	}

	public void closeJedis()
	{
		if (null != jedis)
		{
			jedis.disconnect();
			jedis.close();
		}
	}

	/**
	 * 读取package中的消息
	 * @param ctx
	 * @param msg
	 * @throws Exception
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {
		List<AtsFrameInstruct> atsFrameList = (List<AtsFrameInstruct>)msg;
		//单个帧处理，多个帧处理
		if(atsFrameList.size()==1){
			logger.debug("===AtsHandler.channelRead.单个帧处理");
			handlerFrameMessageInfo(handlerSingleFrameInfo(atsFrameList.get(0)));
		}else if(atsFrameList.size()>1){
			logger.debug("===AtsHandler.channelRead.多个帧处理");
			handlerFrameMessageInfo(handlerMoreFrameInfo(atsFrameList));
		}else{
			logger.warn("frame not have message");
		}
	}

	/**
	 * 处理单个帧信息
	 * packageData解析出单个messageData数据后面统一解析
	 * @param atsFrameList
	 * @throws Exception
	 */
	public List<AtsPDMssageDataInstruct> handlerSingleFrameInfo(AtsFrameInstruct atsFrameList)throws Exception {
		//Package-Data 长度
		int PackageData_length = atsFrameList.getTotal_length();
		int length = 0;
		int message_length= 0;
		byte[] atsPDMssageDataInstructByte = atsFrameList.getPackage_data();
		List<AtsPDMssageDataInstruct> atsPDMssageDataInstructList = new ArrayList<AtsPDMssageDataInstruct>();
		while(true){
			if(length<PackageData_length) {
				length=length+4;
				message_length = DataUtil.getHighLowByteValue(atsPDMssageDataInstructByte[length-4], atsPDMssageDataInstructByte[length-3])-2;
				int message_id = DataUtil.getHighLowByteValue(atsPDMssageDataInstructByte[length-2], atsPDMssageDataInstructByte[length-1]);
				//content长度
				byte[] content = DataUtil.subBytes(atsPDMssageDataInstructByte, length, message_length);
				length = message_length+length;

				AtsPDMssageDataInstruct atsPDMssageDataInstruct = new AtsPDMssageDataInstruct();
				atsPDMssageDataInstruct.setMessage_length(message_length);
				atsPDMssageDataInstruct.setMessage_id(message_id);
				atsPDMssageDataInstruct.setContent(content);
				atsPDMssageDataInstructList.add(atsPDMssageDataInstruct);
			}else{
				break;
			}
		}
		return atsPDMssageDataInstructList;
	}

	/**
	 * 处理多个帧信息
	 * packageData解析出所有的messageData数据后面统一解析
	 * @param atsFrameInstructList
	 * @return
	 * @throws Exception
	 */
	public List<AtsPDMssageDataInstruct> handlerMoreFrameInfo(List<AtsFrameInstruct> atsFrameInstructList)throws Exception {

		int totalLength = 0;
		for(int i=0;i<atsFrameInstructList.size();i++){
			totalLength += atsFrameInstructList.get(i).total_length;
		}
		logger.debug("===handlerMoreFrameInfo.totalLength 总字节长度="+totalLength);
		byte[] atsAllFrameDataArray = new byte[totalLength];
		int num = 0;
		for(int i=0;i<atsFrameInstructList.size();i++){
			for(int j=0;j<atsFrameInstructList.get(i).getPackage_data().length;j++){
				atsAllFrameDataArray[num]=(atsFrameInstructList.get(i).getPackage_data()[j]);
				num++;
			}
		}
		//ALL MssageData 长度
		int allMessageData_length = atsAllFrameDataArray.length;
		List<AtsPDMssageDataInstruct> atsPDMssageDataInstructList = new ArrayList<AtsPDMssageDataInstruct>();
		int length = 0;
		int message_id;
		int message_length= 0;
		while(true){
			if(length<allMessageData_length) {
				length=length+4;
				message_length = DataUtil.getHighLowByteValue(atsAllFrameDataArray[length-4], atsAllFrameDataArray[length-3])-2;
				message_id = DataUtil.getHighLowByteValue(atsAllFrameDataArray[length-2], atsAllFrameDataArray[length-1]);
				//content长度
				byte[] content = DataUtil.subBytes(atsAllFrameDataArray, length, message_length);
				length = message_length+length;

				AtsPDMssageDataInstruct atsPDMssageDataInstruct = new AtsPDMssageDataInstruct();
				atsPDMssageDataInstruct.setMessage_length(message_length);
				atsPDMssageDataInstruct.setMessage_id(message_id);
				atsPDMssageDataInstruct.setContent(content);
				atsPDMssageDataInstructList.add(atsPDMssageDataInstruct);
			}else{
				break;
			}
		}
		return atsPDMssageDataInstructList;

	}

	/**
	 * 识别message类型
	 * @param atsPDMssageDataInstructList
	 * @throws Exception
	 */
	public void handlerFrameMessageInfo(List<AtsPDMssageDataInstruct> atsPDMssageDataInstructList)throws Exception {
		for (AtsPDMssageDataInstruct atsPDMssageDataInstruct:atsPDMssageDataInstructList){
			int messageId = atsPDMssageDataInstruct.getMessage_id();
			if(messageId==AtsMessageIDCodeEnum.MESSAGE_POLLING.getCode()){
				logger.debug("=== 服务端心跳报文 ===");
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_TRAIN_LOCATION.getCode()){
				logger.debug("=== 列车位置信息报文 ===");
				handlerTrainLocationInfo(atsPDMssageDataInstruct);
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_BLOCK_INFO.getCode()){
				logger.debug("=== 列车阻塞信息报文 ===");
				handlerTrainBlockInfo(atsPDMssageDataInstruct);
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_PLANNED_TRAIN_DATA.getCode()){
				logger.debug("=== 计划列车运行信息信息报文 ===");
				handlerPlannedTrainInfo(atsPDMssageDataInstruct);
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_PRACTICAL_TRAIN_DATA.getCode()){
				logger.debug("=== 实际列车运行信息信息报文 ===");
				handlerPracticalTrainInfo(atsPDMssageDataInstruct);
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_DYNAMIC_TRAIN_DATA.getCode()){
				logger.debug("=== =====  DYNAMIC_TRAIN 暂未开发  ======== ===");
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_DATA_START.getCode()){
					logger.debug("=== 接收到头部信息报文 ===");
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_DATA_CONTENT.getCode()){
					logger.debug("=== 接收到内容信息报文 ===");
			}else if(messageId==AtsMessageIDCodeEnum.ATS_CIOS_DATA_END.getCode()){
					logger.debug("=== 接收到尾部信息报文 ===");
			}
		}
	}

	/**
	 * 列车位置信息
	 * @param atsPDMssageDataInstruct
	 */
	public void  handlerTrainLocationInfo(AtsPDMssageDataInstruct atsPDMssageDataInstruct){

		int messageLength = atsPDMssageDataInstruct.getMessage_length();
		logger.debug("===列车位置 信息长度==="+messageLength);
		int messageId = atsPDMssageDataInstruct.getMessage_id();
		logger.debug("===列车位置 信息ID==="+messageId);

		byte[] trainLocationByte = new byte[messageLength];
		trainLocationByte = atsPDMssageDataInstruct.getContent();
		int tranum =trainLocationByte[0]&0xff;
		logger.debug("===列车位置 信息列车数量==="+tranum);

		AtsCiosTrainLocation atsCiosTrainLocation = new AtsCiosTrainLocation();
		atsCiosTrainLocation.setLen(messageLength);
		atsCiosTrainLocation.setMsg_id(messageId);
		atsCiosTrainLocation.setTrain_cnt(tranum);

		if(!(tranum==0)) {
			int singlerLength =(messageLength-1)/tranum;
			byte[] oldData = DataUtil.subBytes(trainLocationByte, 1, messageLength-1);
			int len = 0;
			byte[] newData = new byte[singlerLength];
			for(int j=0;j<oldData.length;j++){
				if(len==singlerLength-1){
					len=0;
					//status字段获取注意bit位置
					byte[] byt = DataUtil.getBooleanArray(newData[9]);

					AtsCiosTrainLocationBitStatus asCiosTrainLocationBitStatus = new AtsCiosTrainLocationBitStatus();
					atsCiosTrainLocation.setTrain_unit_number(DataUtil.getHighLowByteValue(newData[0], newData[1]));
					atsCiosTrainLocation.setTrain_service_number((DataUtil.getHighLowByteValue(newData[2], newData[3])));
					atsCiosTrainLocation.setDestination_id(DataUtil.getHighLowByteValue(newData[4], newData[5]));
					atsCiosTrainLocation.setDirection(newData[6] & 0xff);
					atsCiosTrainLocation.setStation_ID_in_Up_Side(newData[7] & 0xff);
					atsCiosTrainLocation.setStation_ID_in_Down_Side((newData[8] & 0xff));
					//status字段
					asCiosTrainLocationBitStatus.setIsPlansRTrain(byt[0] & 0xff);
					asCiosTrainLocationBitStatus.setIsOnTime("" + (byt[1] & 0xff) + (byt[2] & 0xff));
					atsCiosTrainLocation.setAtsCiosTrainLocationBitStatus(asCiosTrainLocationBitStatus);

					atsCiosTrainLocation.setWindow_handle(DataUtil.getHighLowByteValue(newData[10], newData[11]));
					logger.debug("===列车位置 信息内容 ===" + atsCiosTrainLocation);
				}else{
					newData[len]=oldData[j];
					len++;
				}
			}
		}else{
			logger.debug("===列车位置信息 tranum 数量为0 ==="+atsCiosTrainLocation);
		}
	}

	/**
	 * 列车阻塞信息
	 * @param atsPDMssageDataInstruct
	 */
	public void handlerTrainBlockInfo(AtsPDMssageDataInstruct atsPDMssageDataInstruct){
		int messageLength = atsPDMssageDataInstruct.getMessage_length();
		logger.debug("===列车阻塞 信息长度==="+messageLength);
		int messageId = atsPDMssageDataInstruct.getMessage_id();
		logger.debug("===列车阻塞 信息ID==="+messageId);

;		byte[] trainByte = new byte[messageLength];
		trainByte = atsPDMssageDataInstruct.getContent();
		int unitNumber = trainByte[0]&0xff;
		logger.debug("===列车阻塞 信息列车数量==="+unitNumber);

		AtsCiosBlockInfo atsCiosBlockInfo = new AtsCiosBlockInfo();
		atsCiosBlockInfo.setLen(messageLength);
		atsCiosBlockInfo.setMsg_id(messageId);
		atsCiosBlockInfo.setTrain_cnt(unitNumber);

		if(!(unitNumber==0)){
			int singlerLength =(messageLength-1)/unitNumber;
			byte[] oldData = DataUtil.subBytes(trainByte, 1, messageLength-1);
			int len = 0;
			byte[] newData = new byte[singlerLength];
			for(int j=0;j<oldData.length;j++) {
				if (len == singlerLength-1) {
					len = 0;
					atsCiosBlockInfo.setTrain_unit_number(DataUtil.getHighLowByteValue(newData[0], newData[1]));
					atsCiosBlockInfo.setTrain_service_number(DataUtil.getHighLowByteValue(newData[2], newData[3]));
					atsCiosBlockInfo.setDirection(newData[4] & 0xff);
					atsCiosBlockInfo.setStation_ID_in_Up_Side(newData[5] & 0xff);
					atsCiosBlockInfo.setStation_ID_in_Down_Side(newData[6] & 0xff);
					atsCiosBlockInfo.setStop_flag(newData[7] & 0xff);
					logger.debug("=== 列车位置 信息内容 ===" + atsCiosBlockInfo);
				} else {
					int a = len;
					newData[a] = oldData[j];
					len++;
				}
			}
		}else{
			logger.debug("===列车阻塞信息 tranum 数量为0==="+atsCiosBlockInfo);
		}
	}

	/**
	 * 计划列车信息
	 * @param atsPDMssageDataInstruct
	 */
	public void handlerPlannedTrainInfo(AtsPDMssageDataInstruct atsPDMssageDataInstruct){
		int messageLength = atsPDMssageDataInstruct.getMessage_length();
		logger.debug("===计划列车 信息长度==="+messageLength);
		int messageId = atsPDMssageDataInstruct.getMessage_id();
		logger.debug("===计划列车 信息ID==="+messageId);

		byte[] trainByte = new byte[messageLength];
		trainByte = atsPDMssageDataInstruct.getContent();

		//消息开始
		int Sub_id = DataUtil.getHighLowByteValue(trainByte[0], trainByte[1]);
		int table_id = DataUtil.getHighLowByteValue(trainByte[2], trainByte[3]);
		int trip_cnt = trainByte[4]&0xff;
		logger.debug("===计划列车 信息单程数量==="+trip_cnt);

		//repeatBegin数据
		byte[] oldData = DataUtil.subBytes(trainByte, 5, messageLength-5);

		List<AtsCiosPlannedTrainData> atsCiosPlannedTrainDataList = new ArrayList<AtsCiosPlannedTrainData>();
		//游标
		int a=0;
		if(trip_cnt!=0){
			int rec_cnt=0;
			int train_id;
			int destination_id;
			for(int i=0;i<trip_cnt;i++){
				//算法找出想要的区间的值
				a=a+(rec_cnt*16+8);
				rec_cnt = DataUtil.getHighLowByteValue(oldData[a-2], oldData[a-1]);
				train_id = DataUtil.getFourDigitByteValue(oldData[a-8],oldData[a-7],oldData[a-6],oldData[a-5]);
				destination_id = DataUtil.getHighLowByteValue(oldData[a-4], oldData[a-3]);

				AtsCiosPlannedTrainData atsCiosPlannedTrainData = new AtsCiosPlannedTrainData();
				atsCiosPlannedTrainData.setTrain_id(train_id);
				atsCiosPlannedTrainData.setDestination_id(destination_id);
				atsCiosPlannedTrainData.setRec_cnt(rec_cnt);

				List<AtsCiosPlannedTrainDataRepeat> atsCiosPlannedTrainDataRepeatList =  new ArrayList<AtsCiosPlannedTrainDataRepeat>();
				for(int j=0;j<rec_cnt;j++){

					int station_id = oldData[a+1]&0xff;
					int platform_id = DataUtil.getHighLowByteValue(oldData[a+2],oldData[3]);
					int a_time = DataUtil.getFourDigitByteValue(oldData[a+4],oldData[a+5],oldData[a+6],oldData[a+7]);
					int d_time = DataUtil.getFourDigitByteValue(oldData[a+8],oldData[a+9],oldData[a+10],oldData[a+11]);
					int flag = oldData[a+12]&0xff;
					int reserve = DataUtil.getFourDigitByteValue(oldData[a+13],oldData[a+14],oldData[a+15],oldData[a+16]);

					AtsCiosPlannedTrainDataRepeat atsCiosPlannedTrainDataRepeat = new AtsCiosPlannedTrainDataRepeat();
					atsCiosPlannedTrainDataRepeat.setStation_id(station_id);
					atsCiosPlannedTrainDataRepeat.setPlatform_id(platform_id);
					atsCiosPlannedTrainDataRepeat.setA_time(a_time);
					atsCiosPlannedTrainDataRepeat.setD_time(d_time);
					atsCiosPlannedTrainDataRepeat.setFlag(flag);
					atsCiosPlannedTrainDataRepeat.setReserve(reserve);
					atsCiosPlannedTrainDataRepeatList.add(atsCiosPlannedTrainDataRepeat);
				}
				atsCiosPlannedTrainData.setAtsCiosPlannedTrainDataRepeat(atsCiosPlannedTrainDataRepeatList);
				atsCiosPlannedTrainDataList.add(atsCiosPlannedTrainData);
			}
			logger.debug("===计划列车 信息内容 ===" + atsCiosPlannedTrainDataList);
		}else{
			logger.debug("===计划列车 信息tranum数量为0 ===");
		}
	}

	/**
	 * 实际列车运行信息
	 * @param atsPDMssageDataInstruct
	 */
	public void handlerPracticalTrainInfo(AtsPDMssageDataInstruct atsPDMssageDataInstruct){

		int messageLength = atsPDMssageDataInstruct.getMessage_length();
		logger.debug("===实际列车运行 信息长度==="+messageLength);
		int messageId = atsPDMssageDataInstruct.getMessage_id();
		logger.debug("===实际列车运行 信息ID==="+messageId);

		byte[] trainByte = new byte[messageLength];
		trainByte = atsPDMssageDataInstruct.getContent();

		//消息开始
		int contentSub_id = DataUtil.getHighLowByteValue(trainByte[0], trainByte[1]);
		int group_id = DataUtil.getHighLowByteValue(trainByte[2], trainByte[3]);
		int rec_cnt = DataUtil.getHighLowByteValue(trainByte[4], trainByte[5]);
		logger.debug("===实际列车运行 信息单程数量==="+rec_cnt);

		//repeatBegin数据
		byte[] oldData = DataUtil.subBytes(trainByte, 6, messageLength-6);

		AtsCiosPracticalTrainData atsCiosPracticalTrainData = new AtsCiosPracticalTrainData();
		List<AtsCiosPracticalTrainDataRepeat> atsCiosPracticalTrainDataRepeatList = new ArrayList<AtsCiosPracticalTrainDataRepeat>();

		atsCiosPracticalTrainData.setSub_id(contentSub_id);
		atsCiosPracticalTrainData.setGroup_id(group_id);
		atsCiosPracticalTrainData.setRec_cnt(rec_cnt);

		//游标
		int a=0;
		if(rec_cnt!=0){
			for(int i=0;i<rec_cnt;i++){
				//算法找出想要的区间的值 游标
				a=(i+1)*25;
				int station_id = oldData[a-25]&0xff;
				int platform_id = DataUtil.getHighLowByteValue(oldData[a-24],oldData[a-23]);
				int a_time = DataUtil.getFourDigitByteValue(oldData[a-22],oldData[a-21],oldData[a-20],oldData[a-19]);
				int d_time = DataUtil.getFourDigitByteValue(oldData[a-18],oldData[a-17],oldData[a-16],oldData[a-15]);
				int flag = oldData[a-14]&0xff;
				int train_id = DataUtil.getFourDigitByteValue(oldData[a-13],oldData[a-12],oldData[a-11],oldData[a-10]);
				int table_id = DataUtil.getHighLowByteValue(oldData[a-9],oldData[a-8]);
				int destinnation_id = DataUtil.getHighLowByteValue(oldData[a-7],oldData[a-6]);
				int type = oldData[a-5]&0xff;
				int reserve = DataUtil.getFourDigitByteValue(oldData[a-4],oldData[a-3],oldData[a-2],oldData[a-1]);

				AtsCiosPracticalTrainDataRepeat atsCiosPracticalTrainDataRepeat = new AtsCiosPracticalTrainDataRepeat();
				atsCiosPracticalTrainDataRepeat.setStation_id(station_id);
				atsCiosPracticalTrainDataRepeat.setPlalform_id(platform_id);
				atsCiosPracticalTrainDataRepeat.setA_time(a_time);
				atsCiosPracticalTrainDataRepeat.setD_time(d_time);
				atsCiosPracticalTrainDataRepeat.setFlag(flag);
				atsCiosPracticalTrainDataRepeat.setTable_id(train_id);
				atsCiosPracticalTrainDataRepeat.setTable_id(table_id);
				atsCiosPracticalTrainDataRepeat.setDestination_id(destinnation_id);
				atsCiosPracticalTrainDataRepeat.setType(type);
				atsCiosPracticalTrainDataRepeat.setReverve(reserve);
				atsCiosPracticalTrainDataRepeatList.add(atsCiosPracticalTrainDataRepeat);
			}
			atsCiosPracticalTrainData.setAtsCiosPracticalTrainDataRepeatList(atsCiosPracticalTrainDataRepeatList);
			logger.debug("===实际列车运行 信息内容 ===" + atsCiosPracticalTrainData);
		}else{
			logger.debug("===实际列车运行 信息rec_cnt数量为0 ===");
		}
	}

	/*
	 * 客户端去和服务端连接成功时触发channelActive
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		logger.debug("===链接已经激活===");
		logger.debug("===激活时间是："+new Date()+"===");

//		AtsClient atsClient = new AtsClient();
//		atsClient.sendFasInfo();
//		atsClient.sendScadaInfo();
//		atsClient.sendBasInfo();


	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		logger.debug("===链接已经关闭===");
		logger.debug("===停止时间是："+new Date()+"===");
		
		// 连接断开时调用重连方法
		Reconnection.doConnect();
		
		
		
		
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception{
		cause.printStackTrace();
		//closeJedis();
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		logger.debug("===AtsHandler.userEventTriggered.客户端循环心跳监测发送: "+new Date());
		if (evt instanceof IdleStateEvent){
			IdleStateEvent event = (IdleStateEvent)evt;
			if (event.state()== IdleState.WRITER_IDLE){
				logger.debug("===AtsHandler.userEventTriggered.客户端没有信息发送给服务端  客户端每5秒发送 MESSAGE_POLLING 信息给服务端");
				ByteBuf byteBuf = null;
				byte[] frameHead = new byte[4];
				frameHead[0]=(byte)0xff;
				frameHead[1]=0;
				frameHead[2]=5;
				frameHead[3]=0;
				byte[] message = new byte[4];
				message[0]= 0;
				message[1]= (byte) 2;
				message[2]= (byte) (AtsMessageIDCodeEnum.MESSAGE_POLLING.getCode()>>8);
				message[3]= (byte) (AtsMessageIDCodeEnum.MESSAGE_POLLING.getCode() & 0xFF);
				byte[] frame = ArrayUtils.addAll(frameHead, message);
				byteBuf = ctx.channel().alloc().buffer(8*4);
				byteBuf.writeBytes(frame);
				ctx.writeAndFlush(byteBuf);
			}
		}
	}
}
